﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
#if UNITY_EDITOR
using UnityEngine;
#endif

namespace Hsenl {
    // 使用这种数据库的方式来做存档, 而不是采用传统的方式的最大的原因在于版本兼容性的问题.
    // 使用传统的方式最大的问题就在于新版本处理就存档的问题, 假如你改了存档的数据结构, 那么, 你还是需要保留旧的代码, 或者进行序列化兼容方面的处理, 否则, 无法正常的加载
    // 旧的存档, 而且对于旧的存档的数据的调用, 也会让项目变的混乱和难以维护, 不方便进行存档升级.
    // 所以采用这种类似数据库的形式来存档, 避免了不同版本的存档可能需要不同的版本的代码, 同时, 游戏中不直接使用存档里的数据, 而是通过一个中介进行中转, 避免版本之间的混乱.
    // 
    // 该数据库系统是个非常简单的数据库, 仅仅适用单机游戏的数据存储, 对于网络游戏, 则需要专业的数据库, 例如 Mongodb

    // 使用:
    // - 先指定仓库根目录
    // - 然后加载所有仓库到缓存(并不会完全加载该每个仓库, 只是加载仓库的信息)
    // - 也可以不加载全部, 直接use想要使用的仓库
    [Serializable]
    public class DatabaseManager : Singleton<DatabaseManager> {
        private const string RepositoryExtension = ".hdbrep"; // hsenl db repository

#if UNITY_EDITOR
        [SerializeField]
#endif
        private string _repositoriesRootDir;

        private IDBSerialize _serialize;
        private Dictionary<string, DBRepository> _repositories = new(); // key: path
        private DBRepository _currentRepository;

        public string RepositoriesRootDir => this._repositoriesRootDir;

        public IDBSerialize Serialize => this._serialize;
        public DBRepository CurrentRepository => this._currentRepository;

        public DBRepository[] GetRepositories() {
            return this._repositories.Values.ToArray();
        }

        protected override void OnRegister() {
            this._serialize = new DBSerializeJson();
        }

        public void SetRoot(string rootDir) {
            if (!Directory.Exists(rootDir)) {
                Directory.CreateDirectory(rootDir);
            }

            this._repositoriesRootDir = rootDir;
        }

        private string GetPath(string fileName) {
            if (string.IsNullOrEmpty(this.RepositoriesRootDir)) {
                throw new Exception("Net set Repositories Root Dir");
            }

            var path = Path.Combine(this.RepositoriesRootDir, fileName + RepositoryExtension);
            return path;
        }

        // 加载跟目录下所有的仓库(会清空之前加载的)
        public void LoadAllRepositories() {
            if (string.IsNullOrEmpty(this.RepositoriesRootDir)) {
                throw new Exception("Repositories Root Dir is null");
            }

            this.ClearCache();

            var filePaths = IOHelper.GetAllFiles(this.RepositoriesRootDir, includeSub: false, searchPattern: $"*{RepositoryExtension}");
            foreach (var filePath in filePaths) {
                this.LoadRepository(filePath);
            }
        }

        // 加载一个仓库到缓存中
        private DBRepository LoadRepository(string path) {
            if (this._repositories.TryGetValue(path, out var rep)) {
                Log.Error($"Repository '{path}' is already loaded");
                return null;
            }

            using var fs = new FileStream(path, FileMode.Open, FileAccess.Read);
            fs.Seek(0, SeekOrigin.Begin);
            var bytes = new byte[fs.Length];
            fs.Read(bytes, 0, bytes.Length);

            rep = this._serialize.Deserialize<DBRepository>(bytes);
            if (rep != null) {
                this._repositories.Add(path, rep);
                rep.FileName = Path.GetFileNameWithoutExtension(path);
                rep.FilePath = path;
                rep.SetRoot($"{this._repositoriesRootDir}/{rep.FileName}");
                Log.Info($"Load repository succ. '{path}'");
                return rep;
            }

            return null;
        }

        // 根目录中是否存在某个仓库
        private bool ExistRepository(string path) {
            return File.Exists(path);
        }

        // 有则直接用, 没有则加载
        public DBRepository UseRepository(string name) {
            var path = this.GetPath(name);
            if (this._currentRepository != null && this._currentRepository.FilePath == path) {
                return this._currentRepository;
            }

            // 尝试获取
            var rep = this.GetRepository(name);

            // 尝试加载
            if (rep == null) {
                // 先尝试加载
                rep = this.LoadRepository(path);
            }

            this._currentRepository = rep;

            return rep;
        }

        public DBRepository CreateRepository(string name) {
            if (!PathHelper.IsValidFileNameOrFolderName(name)) {
                Log.Error($"Invalid Repositroy Name:'{name}'");
                return null;
            }
            
            var path = this.GetPath(name);
            if (this.ExistRepository(path)) {
                Log.Error($"Repository '{path}' is already in exist");
                return null;
            }

            var rep = new DBRepository();
            var bin = this._serialize.Serialize(rep);
            using var fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
            fs.SetLength(0);
            fs.Write(bin);

            rep.FileName = name;
            rep.FilePath = path;
            rep.SetRoot($"{this._repositoriesRootDir}/{rep.FileName}");
            this._repositories.Add(path, rep);

            Log.Info($"New repository succ. '{path}'");
            return rep;
        }

        // 从缓存中获取仓库
        public DBRepository GetRepository(string name) {
            var path = this.GetPath(name);
            return this._repositories.TryGetValue(path, out var rep) ? rep : null;
        }

        private void ClearCache() {
            this._currentRepository = null;
            this._repositories.Clear();
        }

        // 同时从缓存和目录中, 删除某个仓库
        public bool DeleteRepository(string name) {
            var path = this.GetPath(name);
            this._repositories.Remove(path, out var deleter);

            if (this._currentRepository == deleter)
                this._currentRepository = null;

            if (File.Exists(path)) {
                File.Delete(path);
                return true;
            }

            return false;
        }
    }
}